//--------------------------------------------------------------------------------------------------
//                                                   ___  __  __  __  __  __  __  __    __  __  ___ 
// Copyright 2010, Koert van der Veer.              / __)(  )(  )(  \/  )(  )(  )(  )  (  )(  )/ __)
// Licensed under the Apache License,              ( (__  )(__)(  )    (  )(__)(  )(__  )(__)( \__ \
// version 2.0; ("the License")                     \___)(______)(_/\/\_)(______)(____)(______)(___/

// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
//     http://www.apache.org/licenses/LICENSE-2.0 
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License. 
//--------------------------------------------------------------------------------------------------
// This file is part of cumulus,  an memory manager implementation in C++.  Details are discussed in
// my blog,  which can be found at http://ondergetekende.nl.  Comments,  suggestions and bug reports
// are welcome. They can be added as comments at the blog. Be advised that in its current form, this
// code is most likely not production ready; it simply hasn't been tested sufficiently. 
//--------------------------------------------------------------------------------------------------
// A layer which adds a per-thread free list. 
//--------------------------------------------------------------------------------------------------

#pragma once

#include "Utils/FakeThreadLocalStorage.h"

namespace Cumulus
{
	template< typename FreeListType, int ThreadCount, typename InnerLayer>
	class PerThreadFreeList : public InnerLayer
	{
	private:
		Utils::FakeThreadLocalStorage< FreeListType, ThreadCount > ThreadFreeList;
		typedef typename Utils::FakeThreadLocalStorage< FreeListType, ThreadCount >::Iterator ThreadFreeListIteratorType;
	
	public:
		// Minimum size is determined by the size of the freelist entries
		static const size_t MinSize = ( FreeListType::MinEntrySize >= InnerLayer::MinSize ) ?
			FreeListType::MinEntrySize :
			InnerLayer::MinSize;

		// We're thread safe if both the inner layer is thread safe and the free list is thread safe
		static const bool IsThreadSafe = InnerLayer::IsThreadSafe;

		~PerThreadFreeList()
		{
			// Iterate over all TLS entries, and free all the entries in the enclosed free list.
			for( ThreadFreeListIteratorType FreelistIterator(ThreadFreeList); 
				!FreelistIterator.IsAtEnd(); FreelistIterator++ )
			{
				FreeListType::Iterator it( *FreelistIterator ); 
				while( !it.IsAtEnd() )
				{
					InnerLayer::Free( *it );
				}
			}
		}


		void* Malloc( size_t& Size ) 
		{
			// Enforce minimum size
			if( Size < FreeListType::MinEntrySize )
			{
				Size = FreeListType::MinEntrySize;
			}

			void* Result = NULL;
			if( ThreadFreeList.IsAvailable() )
			{
				Result = ThreadFreeList->Get( Size );
			}

			if( Result == NULL )
			{
				return InnerLayer::Malloc( Size );
			}
			else
			{
				return Result;
			}
		}

		void Free( void* Memory ) 
		{
			size_t Size = InnerLayer::GetSize( Memory );

			bool WasAddedToFreeList = FALSE;
			if( ThreadFreeList.IsAvailable() )
			{
				WasAddedToFreeList = ThreadFreeList->Add( Memory, Size );
			}

			if( !WasAddedToFreeList )
			{
				InnerLayer::Free( Memory );
			}
		}
	};
}